﻿//-----------------------------------------------------------------------
// <copyright file="ConnectionForm.cs" company="MSCRMTools">
//     Copyright © MSCRMTools. All rights reserved.
// </copyright>
// <author>tanguy92</author>
// <date>07/04/2011</date>
// <codeplexurl>http://searchablepropertyupdater.codeplex.com</codeplexurl>
// <summary>no summary</summary>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using CrmConnectionProject;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

namespace CrmConnectionProject
{
    /// <summary>
    /// Formulaire de création et d'édition d'une connexion
    /// à un serveur Crm.
    /// </summary>
    public partial class ConnectionForm : Form
    {
        #region Variables

        /// <summary>
        /// Détail de connexion courant
        /// </summary>
        ConnectionDetail detail;

        /// <summary>
        /// List of Crm server organizations
        /// </summary>
        List<OrganizationDetail> _organizations;

        /// <summary>
        /// Indique si l'utilisateur a demandé à se connecter
        /// au serveur
        /// </summary>
        bool doConnect = false;

        #endregion

        #region Propriétés

        /// <summary>
        /// Obtient la valeur qui définit si l'utilisateur a demandé 
        /// à se connecter au serveur
        /// </summary>
        public bool DoConnect
        {
            get { return doConnect; }
        }

        /// <summary>
        /// Obtient ou définit le détail de la connexion courante
        /// </summary>
        public ConnectionDetail CrmConnectionDetail
        {
            get { return detail; }
            set { detail = value; }
        }

        bool isCreationMode;

        #endregion

        #region Constructeur

        /// <summary>
        /// Créé une nouvelle instance de la classe ConnectionForm
        /// </summary>
        public ConnectionForm(bool isCreation)
        {
            InitializeComponent();
            this.isCreationMode = isCreation;

            ToolTip tip = new ToolTip();
            tip.ToolTipTitle = "Information";
            tip.SetToolTip(this.tbServerName, "For CRM Online, use:\r\ncrm.dynamics.com for North America\r\ncrm4.dynamics.com for EMEA\r\ncrm5.dynamics.com for Asia Pacific\r\n\r\nFor OnPremise:\r\nUse the server name\r\n\r\nFor IFD:\r\nUse <organization_name>.<domain>.<extension>");
            tip.SetToolTip(this.tbServerPort, "Specify port only if different from 80 or 443 (SSL)");

            // allows for validation of SSL conversations
            ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);
        }

        // callback used to validate the certificate in an SSL conversation
        private static bool ValidateRemoteCertificate(
        object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors policyErrors
        )
        {
           return true;
        }


        #endregion

        #region Méthodes

        protected override void OnLoad(EventArgs e)
        {
            if (detail != null)
            {
                FillValues();
            }

            base.OnLoad(e);
        }

        private void bValidate_Click(object sender, EventArgs e)
        {
            if (this.tbName.Text.Length == 0)
            {
                MessageBox.Show(this, "You must define a name for this connection!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (this.comboBoxOrganizations.Text.Length == 0 && this.comboBoxOrganizations.SelectedItem == null && !this.cbUseIfd.Checked)
            {
                MessageBox.Show(this, "You must select an organization!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (this.tbUserPassword.Text.Length == 0 && (cbUseIfd.Checked || rbAuthenticationCustom.Checked))
            {
                MessageBox.Show(this, "You must define a password!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            //if (this.tbHomeRealmUri.Text.Length == 0 && (cbUseIfd.Checked))
            //{
            //    MessageBox.Show(this, "You must define a security token service server url!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            //    return;
            //}

            if (detail == null)
                detail = new ConnectionDetail();

            // Save connection details in structure
            detail.ConnectionName = this.tbName.Text;
            detail.IsCustomAuth = this.rbAuthenticationCustom.Checked;
            detail.UseSsl = this.cbUseSsl.Checked;
            detail.UseOnline = this.cbUseOnline.Checked;
            detail.ServerName = this.tbServerName.Text;
            detail.ServerPort = this.tbServerPort.Text;
            detail.UserDomain = this.tbUserDomain.Text;
            detail.UserName = this.tbUserLogin.Text;
            detail.UserPassword = this.tbUserPassword.Text;
            detail.UseIFD = this.cbUseIfd.Checked;

            try
            {
                FillDetails();

                if (MessageBox.Show(this, "Do you want to connect now to this server?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    doConnect = true;
                }

                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void bCancel_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }

        private void comboBoxOrganizations_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.comboBoxOrganizations.Text.Length > 0 || this.comboBoxOrganizations.SelectedItem != null)
            {
                this.bValidate.Enabled = true;
            }
            else
            {
                this.bValidate.Enabled = false;
            }
        }

        private void comboBoxOrganizations_TextChanged(object sender, EventArgs e)
        {
            if (this.comboBoxOrganizations.Text.Length > 0 || this.comboBoxOrganizations.SelectedItem != null)
            {
                this.bValidate.Enabled = true;
            }
            else
            {
                this.bValidate.Enabled = false;
            }
        }

        private void cbUseIfd_CheckedChanged(object sender, EventArgs e)
        {
            if (this.cbUseIfd.Checked)
            {
                this.cbUseOnline.Checked = false;
            }

            this.bValidate.Enabled = this.cbUseIfd.Checked;

            this.rbAuthenticationCustom.Checked = cbUseIfd.Checked;
            this.rbAuthenticationIntegrated.Enabled = !cbUseIfd.Checked;
            this.rbAuthenticationIntegrated.Checked = !cbUseIfd.Checked;

            this.tbUserDomain.Enabled = cbUseIfd.Checked;
            this.tbUserLogin.Enabled = cbUseIfd.Checked;
            this.tbUserPassword.Enabled = cbUseIfd.Checked;

            this.bGetOrganizations.Enabled = !cbUseIfd.Checked;
            this.cbUseSsl.Checked = cbUseIfd.Checked;
            this.cbUseSsl.Enabled = !cbUseIfd.Checked;
        }

        private void cbUseOnline_CheckedChanged(object sender, EventArgs e)
        {
            if (this.cbUseOnline.Checked)
            {
                this.cbUseIfd.Checked = false;

                this.rbAuthenticationCustom.Checked = true;
                this.rbAuthenticationIntegrated.Enabled = false;
                this.rbAuthenticationIntegrated.Checked = false;

                this.tbUserDomain.Enabled = false;
                this.tbUserLogin.Enabled = true;
                this.tbUserPassword.Enabled = true;

                this.bGetOrganizations.Enabled = false;
                this.cbUseSsl.Checked = true;
                this.cbUseSsl.Enabled = false;
                this.tbServerPort.Enabled = false;
            }
            else
            {
                this.rbAuthenticationCustom.Checked = false;
                this.rbAuthenticationIntegrated.Enabled = true;
                this.rbAuthenticationIntegrated.Checked = true;

                this.tbUserDomain.Enabled = false;
                this.tbUserLogin.Enabled = false;
                this.tbUserPassword.Enabled = false;

                this.bGetOrganizations.Enabled = true;
                this.cbUseSsl.Checked = false;
                this.cbUseSsl.Enabled = true;
                this.tbServerPort.Enabled = true;
            }

            this.bGetOrganizations.Enabled = this.cbUseOnline.Checked;

        }

        private void rbAuthenticationIntegrated_CheckedChanged(object sender, EventArgs e)
        {
            this.tbUserDomain.Enabled = this.rbAuthenticationCustom.Checked;
            this.tbUserLogin.Enabled = this.rbAuthenticationCustom.Checked;
            this.tbUserPassword.Enabled = this.rbAuthenticationCustom.Checked;
        }

        private void bGetOrganizations_Click(object sender, EventArgs e)
        {
            string warningMessage = string.Empty;
            bool goodAuthenticationData = false;
            bool goodServerData = false;

            // Check data filled by user
            if (rbAuthenticationIntegrated.Checked ||
                (
                rbAuthenticationCustom.Checked &&
                (tbUserDomain.Text.Length > 0 || cbUseIfd.Checked) &&
                tbUserLogin.Text.Length > 0 &&
                tbUserPassword.Text.Length > 0
                )
                ||
                    (this.cbUseOnline.Checked && !string.IsNullOrEmpty(this.tbUserLogin.Text) &&
                    !string.IsNullOrEmpty(this.tbUserPassword.Text)))
                goodAuthenticationData = true;

            if (tbServerName.Text.Length > 0)
                goodServerData = true;

            if (!goodServerData)
            {
                warningMessage += "Please provide server name\r\n";
            }
            if (!goodAuthenticationData)
            {
                warningMessage += "Please fill all authentication textboxes\r\n";
            }

            if (warningMessage.Length > 0)
            {
                MessageBox.Show(this, warningMessage, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                // Save connection details in structure
                if (this.isCreationMode)
                {
                    detail = new ConnectionDetail();
                }
                
                detail.ConnectionName = this.tbName.Text;
                detail.IsCustomAuth = this.rbAuthenticationCustom.Checked;
                detail.UseSsl = this.cbUseSsl.Checked;
                detail.ServerName = this.tbServerName.Text;
                detail.ServerPort = this.tbServerPort.Text;
                detail.UserDomain = this.tbUserDomain.Text;
                detail.UserName = this.tbUserLogin.Text;
                detail.UserPassword = this.tbUserPassword.Text;
                detail.UseIFD = this.cbUseIfd.Checked;
                detail.UseOnline = this.cbUseOnline.Checked;
                
                detail.AuthType = AuthenticationProviderType.ActiveDirectory;
                if (cbUseIfd.Checked)
                {
                    detail.AuthType = AuthenticationProviderType.Federation;
                }
                else if (cbUseOnline.Checked)
                {
                    detail.AuthType = AuthenticationProviderType.LiveId;
                }

                // Launch organization retrieval
                this.comboBoxOrganizations.Items.Clear();
                Thread retrieveOrgThread = new Thread(new ThreadStart(FillOrganizations));
                retrieveOrgThread.Start();
            }
        }

        /// <summary>
        /// Method to fill organization combobox
        /// </summary>
        /// <param name="organizationList">Combobox</param>
        /// <param name="detail">Connection details</param>
        public void FillOrganizations()
        {
            try
            {
                Guid applicationId = new Guid("6f4cad4a-e3d4-41d7-8ac7-cd17a69c3997");// Guid.NewGuid();//

                SetCursor(this, Cursors.WaitCursor);

                ServerConnection sc = new ServerConnection();

                sc.config = sc.GetServerConfiguration(detail, applicationId);

                // TODO to test at alsy's office
                //System.Net.HttpWebRequest.DefaultWebProxy = WebProxy.GetDefaultProxy();
                //WebProxy proxy = new WebProxy("tmggateway.alsy.fr:8080", true);
                //proxy.Credentials = new NetworkCredential("tanguy.touzard", "", "ste_alsy");
                //System.Net.HttpWebRequest.DefaultWebProxy = proxy;

                using (DiscoveryServiceProxy serviceProxy = new DiscoveryServiceProxy(sc.config.DiscoveryUri, sc.config.HomeRealmUri, sc.config.Credentials, sc.config.DeviceCredentials))
                {
                    // Obtain organization information from the Discovery service. 
                    if (serviceProxy != null)
                    {
                        _organizations = new List<OrganizationDetail>();

                        // Obtain information about the organizations that the system user belongs to.
                        OrganizationDetailCollection orgs = sc.DiscoverOrganizations(serviceProxy);

                        foreach (OrganizationDetail orgDetail in orgs)
                        {
                            _organizations.Add(orgDetail);

                            AddItem(comboBoxOrganizations, orgDetail.FriendlyName);
                        }
                    }
                }

                SetEnableState(this.comboBoxOrganizations, true);
            }
            catch (Exception error)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(error, false);

                DisplayMessageBox(this, "Error while retrieving organizations : " + errorMessage + "\r\n" + error.InnerException.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                SetCursor(this, Cursors.Default);
            }
        }

        /// <summary>
        /// Remplit les contrôles du formulaire avec les données
        /// du détail de connexion courant
        /// </summary>
        private void FillValues()
        {
            this.rbAuthenticationCustom.Checked = detail.IsCustomAuth;
            this.rbAuthenticationIntegrated.Checked = !detail.IsCustomAuth;

            //this.rbAuthenticationIntegrated.CheckedChanged += new EventHandler(rbAuthenticationIntegrated_CheckedChanged);

            this.tbName.Text = detail.ConnectionName;
            this.tbServerName.Text = detail.ServerName;
            this.tbServerPort.Text = detail.ServerPort;
            this.tbUserDomain.Text = detail.UserDomain;
            this.tbUserLogin.Text = detail.UserName;
            this.tbUserPassword.Text = detail.UserPassword;
            this.comboBoxOrganizations.Text = detail.OrganizationFriendlyName;
            this.cbUseIfd.Checked = detail.UseIFD;
            this.cbUseOnline.Checked = detail.UseOnline;
            this.cbUseSsl.Checked = detail.UseSsl;
        }

        /// <summary>
        /// Remplit le détail de connexion avec le contenu 
        /// des contrôles du formulaire
        /// </summary>
        /// <returns></returns>
        private void FillDetails()
        {
            bool hasFoundOrg = false;

            string selectedOrganization = string.Empty;

            if (this.cbUseIfd.Checked)
            {
                selectedOrganization = this.tbServerName.Text.Split('.')[0];
            }
            else
            {
                selectedOrganization = comboBoxOrganizations.SelectedItem != null ? comboBoxOrganizations.SelectedItem.ToString() : comboBoxOrganizations.Text;
            }

            if (_organizations == null || _organizations.Count == 0)
            {
                FillOrganizations();
            }

            foreach (OrganizationDetail organization in _organizations)
            {
                if ((organization.FriendlyName == selectedOrganization && !detail.UseIFD) ||
                    (organization.UniqueName.ToLower() == selectedOrganization.ToLower() && detail.UseIFD))
                {
                    detail.OrganizationServiceUrl = organization.Endpoints[EndpointType.OrganizationService];
                    detail.Organization = organization.UniqueName;
                    detail.OrganizationFriendlyName = organization.FriendlyName;

                    detail.ConnectionName = this.tbName.Text;

                    if (this.isCreationMode)
                    {
                        detail.ConnectionId = Guid.NewGuid();
                    }

                    hasFoundOrg = true;

                    break;
                }
            }

            if (!hasFoundOrg)
            {
                throw new Exception("Unable to validate user inputs");
            }
        }

        #endregion

        #region Méthodes déléguées

        public static void AddItem(ComboBox combobox, object item)
        {
            MethodInvoker miAddItem = delegate
            {
                combobox.Items.Add(item);
            };

            if (combobox.InvokeRequired)
            {
                combobox.Invoke(miAddItem);
            }
            else
            {
                miAddItem();
            }
        }

        internal static void SetEnableState(Control control, bool enabled)
        {
            MethodInvoker miSetEnableState = delegate
            {
                control.Enabled = enabled;
            };

            if (control.InvokeRequired)
            {
                control.Invoke(miSetEnableState);
            }
            else
            {
                miSetEnableState();
            }
        }

        public static void SetCursor(Control control, Cursor cursor)
        {
            MethodInvoker miSetCursor = delegate
            {
                control.Cursor = cursor;
            };

            if (control.InvokeRequired)
            {
                control.Invoke(miSetCursor);
            }
            else
            {
                miSetCursor();
            }
        }

        public static DialogResult DisplayMessageBox(Form form, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
        {
            DialogResult result = DialogResult.Ignore;

            MethodInvoker DisplayMessage = delegate
            {
                result = MessageBox.Show(form, message, caption, buttons, icon);
            };

            if (form.InvokeRequired)
            {
                form.Invoke(DisplayMessage);
                return result;
            }
            else
            {
                DisplayMessage();
                return result;
            }
        }

        #endregion
    }
}